package com.blockmeta.bbs.businesslibrary.websocket;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Handler;
import android.os.Message;
import com.blankj.utilcode.util.LogUtils;

import com.blankj.utilcode.util.LogUtils;
import com.blockmeta.bbs.baselibrary.base.app.BaseApp;
import com.blockmeta.bbs.baselibrary.utils.SecurityUtil;
import com.blockmeta.bbs.businesslibrary.business.DataManager;
import com.blockmeta.bbs.businesslibrary.pojo.websocket.AuthSocketPOJO;
import com.blockmeta.bbs.businesslibrary.pojo.websocket.BasePOJO;
import com.blockmeta.bbs.businesslibrary.pojo.websocket.ChartUpdate;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import buf.Common;
import buf.Kline;
import buf.NotifyOuterClass;
import buf.Return;


public class ConnectManager implements IUIEntrance {


    private static final int MSG_DELAY_CLOSE = 0x01; // 延迟关闭



    public enum ConnCode {
        Init(0), Start(1), Fail(2), Connect(3), ConnectChallenge(4), ConnectResponse(5), ConnectStatus(6), Ready(7);

        private int m_value;

        ConnCode(int value) {
            m_value = value;
        }

        int getValue() {
            return m_value;
        }
    }

    // 連線Server類型
    public enum ServerType {
        Login, // 登录服务器
        Quote, // 行情统一服务器
        Trade, // 交易服务器
    }

    // 連線模式

    /**
     * active 代表当前连线正常，有心跳
     * link 代表当前连线进入后台，无心跳，服务器如果过了timeout时间没有收到心跳就会把连线踢掉
     * standby 代表备用，没有建立连接，但connnectmanager存在
     */
    public enum ConnType {
        Active, Link, Standby
    }


    protected ServerType m_enServerType;
    protected ConnType m_enConnType;
    protected ConnCode m_enConnCode;
    protected Market m_market;
    protected BlockMetaSocket m_socServer = null;
    protected static AtomicInteger m_nPktNo;
    protected static AtomicInteger m_nSeqNo;
    protected HeartBeat m_hb;
    protected WaitSendQueue m_waitQueue;
    protected Handler m_handler;
    protected Runnable m_run, m_linkClose;
    protected ConnectInfo m_connectInfo;// HashMap<IDC, ConnectInfo>


    String authValue = "";
    protected LinkQueue m_linkQ;  // 保留 連線後發出的封包
    private ConnectCenter m_connectCenter;
    private ReentrantReadWriteLock m_lock;


    static {
        m_nPktNo = new AtomicInteger(0);
        m_nSeqNo = new AtomicInteger(0);
    }

    public ConnectManager(ServerType type, ConnType connType, Market market) {
        super();

        init(type, market, connType);
    }


    protected void init(ServerType type, Market market, ConnType connType) {
        m_enServerType = type;
        m_enConnType = connType;
        m_market = market;
        m_enConnCode = ConnCode.Init;
        m_connectCenter = ConnectCenter.getConnectCenter();
        m_lock = new ReentrantReadWriteLock();

        m_handler = new InternalHandler();
        m_socServer = new BlockMetaSocket();
        m_linkQ = new LinkQueue(this);
        m_connectInfo = new ConnectInfo();



        m_socServer.setPacketEventListener(new BlockMetaSocket.IPacketEventListener() {
            @Override
            public void onPacket(int what, int session, Object objPacket, int type) {
                switch (what) {
                    case BlockMetaSocket.WHAT_CONNTSTATUS:
                        processConnect(type, session, (String) objPacket);
                        break;
                    case BlockMetaSocket.WHAT_PACKET:
//                        processPacket((BasePOJO) objPacket);
                        processPacket((Return.NormalReturn) objPacket);
                        break;
                }
            }
        });

        m_waitQueue = new WaitSendQueue(this);
        m_hb = new HeartBeat(this);
        m_hb.setHBListener(new HeartBeat.IHBListener() {
            @Override
            public void onDisConnect() {

//                    closeConnect();
            }

            @Override
            public void onAliveStatus() {

            }
        });

//        if (shouldDispatchEvent())
//        {
//            m_connectCenter.getEventBus().post(new Event_ChangeConnType(market, connType));
//        }
    }


    @Override
    public void onEnterLink() {

    }

    @Override
    public void onExitLink() {

    }

    public void initConnect() {
        if (m_enConnCode == ConnCode.Init) {
            onStatus(ConnCode.Start);
        }


    }

    public void startConnect() {
        onStatus(ConnCode.Start);


    }

    public void sendResume() {

    }

    protected void processConnect(int type, int session, String source) {
        String code = "LOGIN";
        if (m_market != null)
            code = m_market.getCode();

        switch (type)
        // size : Connect Status
        {
            case BlockMetaSocket.CONNECT_OK:
                LogUtils.d("author"+ "Connect CONNECT_OK : " + code);
                if (m_enConnCode != ConnCode.Ready) {
                    onStatus(ConnCode.Connect);
                }
                break;
            case BlockMetaSocket.CONNECT_DISCONNECT:
                LogUtils.d("author"+ "Connect CONNECT_DISCONNECT : " + code);
                onStatus(ConnCode.Fail);
                break;
            case BlockMetaSocket.CONNECT_CLOSE:
                LogUtils.d("author"+ "Connect CONNECT_CLOSE: " + code);
                closeConnect(true);
                LogUtils.d("css", "[ConnectManager]" + m_market + "processConnect");
                break;

        }
    }

    protected void processPacket(Return.NormalReturn packet) {
        LogUtils.d("author"+ "processPacket packet.getEvent()" + packet.getEvent());


        switch (packet.getEvent()) {
            case "connect":
                if (m_enConnCode != ConnCode.Ready) {
                    processPacket(packet.getConnectData());
                }
                break;
            case "hb":
                m_hb.processAliveStatus();
            default:
                m_waitQueue.checkUpdate(packet);
                DataManager.getDataManager().processPacket(packet, this);
                break;
        }
    }





    protected void processPacket(Kline.KlineList packet) {
        LogUtils.d("author"+ "processPacket " + packet.toString());

    }

    protected void processPacket(Common.Connect packet) {
        LogUtils.d("author"+ "processPacket " + packet.toString());
        authValue = packet.getNum();
        onStatus(ConnCode.Ready);
//        onStatus(ConnCode.ConnectChallenge);
    }

    protected void processPacket(NotifyOuterClass.Notify packet) {
        LogUtils.d("author"+ "processPacket " + packet.toString());
        onStatus(ConnCode.ConnectStatus);

    }


    protected void processPacket(ChartUpdate packet) {

    }

    protected void onStatus(ConnCode nCode) {
        m_enConnCode = nCode;
        LogUtils.d("author"+ "onStatus" + m_enConnCode);

        switch (nCode) {
            case Start:
                connectServer();
                break;
            case Connect:
//                sendConnect();

                break;
            //用于鉴权协商
            case ConnectChallenge:
                sendConnectResponse();
                break;
            case ConnectResponse:
                break;
            case ConnectStatus:
                notifyConnectFinish();
                onStatus(ConnCode.Ready);
                break;
            case Ready:
                onConnectReady();

                break;
            case Fail:
                onConnectFail();

                break;
            default:
                break;
        }
    }


    protected void connectServer() {
        try {
            m_lock.readLock().lock();


            if (m_connectInfo != null) {
                if (m_enConnType != ConnType.Standby && !isConnectOK()) {

                    m_socServer.openSock(m_connectInfo.m_strIP, m_connectInfo.m_nPort,
                            generateRandomNumber());

                }
            }

        } finally {
            m_lock.readLock().unlock();
        }
    }

    protected void sendConnectResponse() {
        AuthSocketPOJO pkt = new AuthSocketPOJO();

        String sha1 = SecurityUtil.sha1hash(String.format("rekcit-ctb8-%s", authValue));

        pkt.setNum(sha1);

        pkt.set_pt("auth");

        writeConnect(pkt);

        onStatus(ConnCode.ConnectResponse);
    }

    protected boolean writeConnect(BasePOJO packet) {
        if (m_socServer != null) {
            return m_socServer.writePacket(getPktNo(), packet);
        }
        return false;
    }

    protected void onConnectReady() {

        m_connectCenter.post(new Event_ConnectOK(m_market, this));


        m_linkQ.sendAll();

        //发送重新订阅信息


//        if (m_callBack != null)
//        {
//            m_callBack.onSuccess();
//            m_callBack = null;
//        }
    }

    protected void onConnectFail() {
        m_enConnCode = ConnCode.Fail;

        m_connectCenter.post(new Event_ConnectClose(m_market, this, false));

        connectNextServer();
//        if (m_callBack != null)
//        {
//            m_callBack.onFail();
//        }
    }

    protected void sendConnect() {
//        ConnectPacket pkt = new ConnectPacket();
//
//        pkt.m_seq = ConnectManager.getSeqNo();
////        pkt.m_appid = AppEnv.getAppName() + "-Android";
//        pkt.m_appid = AppEnv.getAppName();
//        pkt.m_clientid = getDataManager().getLoginCenter().getClientID();
//        pkt.m_ver = getDataManager().getLoginCenter().getVersionNo();
//
//        writeConnect(pkt);
//        waitConnectFinish();
    }

    protected void waitConnectFinish() {
        m_run = new Runnable() {
            @Override
            public void run() {
                closeConnect(false);
            }
        };

        m_handler.postDelayed(m_run, 10000);
    }

    // 準備終止Link連線
    protected void waitLinkClose() {
        m_linkClose = new Runnable() {
            @Override
            public void run() {
//                if (getConnType() == ConnType.Link)
//                {
//                    sendSuspend(true);
//                }
            }
        };

        m_handler.postDelayed(m_linkClose, 30000);
    }

    protected void notifyConnectFinish() {
        if (m_run != null) {
            m_handler.removeCallbacks(m_run);
        }
    }

    public boolean write(BasePOJO packet) {
        if (m_socServer != null) {
            if (m_enConnCode == ConnCode.Ready) {
                return m_socServer.writePacket(getPktNo(), packet);
            } else {
                LinkQueue.QueuePacket pkt = new LinkQueue.QueuePacket(LinkQueue.QueueType.Write, 0, packet, null);
                saveToQueue(pkt);
            }
        }
        LogUtils.d("write false : " + packet.get_pt() + ", m_enConnCode : " + m_enConnCode);
        LogUtils.d("author"+ "write false : " + packet.get_pt() + ", m_enConnCode : " + m_enConnCode);
        return false;
    }

    public boolean writeAndWait(BasePOJO packet, String repPt, WaitSendQueue.IWaitListener listener) {
        if (m_socServer != null) {
            if (m_enConnCode == ConnCode.Ready) {
                m_waitQueue.addPacket(packet, repPt, listener);
                return m_socServer.writePacket(getPktNo(), packet);
            } else {
                LinkQueue.QueuePacket pkt = new LinkQueue.QueuePacket(LinkQueue.QueueType.Write, 0, packet, null);
                saveToQueue(pkt);
            }
        }
        LogUtils.d("author"+ "writeAndWait false : " + packet.get_pt());
        return false;
    }


    public static int getPktNo() {
        return m_nPktNo.incrementAndGet();
    }

    public static int getSeqNo() {
        return m_nSeqNo.incrementAndGet();
    }

    public static String getSeqNoValue() {
        return String.valueOf(m_nSeqNo);
    }

    public boolean isConnectOK() {
        return m_enConnCode == ConnCode.Ready;
    }

    /**
     * 中斷連線
     */
    public void closeConnect(boolean needReconnect) {
        m_socServer.closeSock(true);
        onConnectClose(needReconnect);
    }

    protected void onConnectClose(boolean needReconnect) {
        m_enConnCode = ConnCode.Fail;
        m_connectCenter.post(new Event_ConnectClose(m_market, this, true));
        if(needReconnect) {
            connectNextServer();
        }

    }

    public synchronized void connectNextServer() {

        if (m_enConnType != ConnType.Standby)    // Standby狀態不重新連線
        {
            m_handler.removeCallbacksAndMessages(null);
            m_handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    LogUtils.d("author"+ "connectNextServer");
                    //如果网络是好的直接重连，如果没有网络就循环请求该方法直到有网络
                    m_handler.removeCallbacksAndMessages(null);
                    if(m_enConnCode != ConnCode.Ready) {
                        if (isNetworkConnected(BaseApp.getApp())) {
                            startConnect();
                        } else {
                            connectNextServer();
                        }
                    }
                }
            }, 2000);
        }
    }

    //检查网络是否连接
    private boolean isNetworkConnected(Context context) {
        if (context != null) {
            ConnectivityManager mConnectivityManager = (ConnectivityManager) context
                    .getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo mNetworkInfo = mConnectivityManager
                    .getActiveNetworkInfo();
            if (mNetworkInfo != null) {
                return mNetworkInfo.isAvailable();
            }
        }
        return false;
    }

    protected void saveToQueue(LinkQueue.QueuePacket packet) {
        m_linkQ.push(packet);
    }

    protected void loadServer(ConnectInfo info) {
        loadServer(info, true);
    }

    protected void loadServer(ConnectInfo info, boolean checkLive) {
        boolean simNeedReconnect = false;


        try {
            m_lock.writeLock().lock();
            simNeedReconnect = m_connectInfo.update(info);

        } finally {
            m_lock.writeLock().unlock();
        }

        if (simNeedReconnect) {
            // 斷線 自動重新連線
            closeConnect(true);
        }
    }


    private int generateRandomNumber() {
        return (int) (Math.random() * Integer.MAX_VALUE);
    }


    public static class Event_LoginConnectClose {
        public ConnectManager m_connMgr;
        public boolean m_bServerClose;

        Event_LoginConnectClose(ConnectManager mgr, boolean close) {
            m_bServerClose = close;
            m_connMgr = mgr;
        }
    }

    public static class Event_ConnectOK {
        public Market m_market;
        public ConnectManager m_connMgr;

        Event_ConnectOK(Market market, ConnectManager mgr) {
            m_market = market;
            m_connMgr = mgr;
        }
    }

    public static class Event_ConnectClose {
        public Market m_market;
        public ConnectManager m_connMgr;
        public boolean m_bServerClose;

        Event_ConnectClose(Market market, ConnectManager mgr, boolean close) {
            m_bServerClose = close;
            m_connMgr = mgr;
            m_market = market;
        }
    }


    public static class Event_ChangeConnType {
        public Market m_market;
        public ConnType m_enType;

        Event_ChangeConnType(Market market, ConnType type) {
            m_enType = type;
            m_market = market;
        }
    }


    private class InternalHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_DELAY_CLOSE:
                    closeConnect(false);
                    break;
            }
        }
    }
}